package com.zh.yqbj.aspect;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zh.yqbj.AsyncManager.AsyncManager;
import com.zh.yqbj.VO.LoginVO;
import com.zh.yqbj.VO.UserVO;
import com.zh.yqbj.annotation.OperationLog;
import com.zh.yqbj.constant.OperationConstant;
import com.zh.yqbj.entity.User;
import com.zh.yqbj.entity.UserLog;
import com.zh.yqbj.enums.OperationType;
import com.zh.yqbj.exception.accountExcption.UnknownAccountException;
import com.zh.yqbj.service.UserLogService;
import com.zh.yqbj.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.Objects;
import java.util.TimerTask;


@Aspect
@Component
@Slf4j
public class OperationLoggerAspect {

    @Autowired
    private UserService userService;

    @Autowired
    private UserLogService userLogService;

    @Pointcut("@annotation(com.zh.yqbj.annotation.OperationLog)")
    public void operationLogPC() {
    }

    /**
     * 处理在登出前的记录
     * 如果是登出操作，需要在登出之前记录
     */
    @Before("operationLogPC()")
    @Transactional(rollbackFor = Exception.class)
    public void before(JoinPoint joinPoint) {
        log.info("进入before切点");
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        OperationLog operationLog = method.getAnnotation(OperationLog.class);
        String value = operationLog.value();
        OperationType type = operationLog.type();
        if (type.equals(OperationType.ONLINE)) {
            if (value.equals(OperationConstant.OPERATION_LOGOUT)) {
                this.logoutLog(value);
            }
            //如果有其他
        }
        if (type.equals(OperationType.OFFLINE)) {
            //如果有其他
        }
    }

    /**
     * 处理在线时候的操作
     *
     * @param joinPoint 切入点
     */
    @After("operationLogPC()")
    @Transactional(rollbackFor = Exception.class)
    public void after(JoinPoint joinPoint) {
        log.info("进入after切点");
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        OperationLog operationLog = method.getAnnotation(OperationLog.class);
        OperationType type = operationLog.type();
        String value = operationLog.value();
        // 判断 什么记录需要在方法结束后事务提交前执行 目前没有
    }

    /**
     * 处理在登陆后的操作
     * 需要获取返回值的操作
     *
     * @param joinPoint 切入点
     * @param result    方法返回值
     */
    @AfterReturning(value = "operationLogPC()", returning = "result")
    @Transactional(rollbackFor = Exception.class)
    public void afterReturning(JoinPoint joinPoint, Object result) {
        log.info("进入afterReturning切点");
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        OperationLog operationLog = method.getAnnotation(OperationLog.class);
        OperationType type = operationLog.type();
        String value = operationLog.value();
        if (type.equals(OperationType.OFFLINE)) {
            if (value.equals(OperationConstant.OPERATION_LOGIN)) {
                //1. 登录后
                this.loginLog(value, result);
            }
            if (value.equals(OperationConstant.OPERATION_REGISTER)) {
                //2. 注册后
                this.registerLog(value, result);
            }
        }
    }

    private void logoutLog(String value) {
        String userId = StpUtil.getLoginIdAsString();
        Device device = getDevice();
        User one = userService.getOne(new LambdaQueryWrapper<User>().eq(User::getId, userId));
        UserLog userLog = BeanUtil.copyProperties(one, UserLog.class, "id");
        userLog.setUserId(userId);
        userLog.setOpDetail(value);
        userLog.setUserPhone(one.getPhone());
        userLog.setUserEmail(one.getEmail());
        userLog.setOpIp(device.clientIP);
        userLog.setOpOs(device.os);
        userLog.setOpTime(LocalDateTime.now());
        AsyncManager.me().execute(new TimerTask() {
            @Override
            public void run() {
                userLogService.save(userLog);
            }
        });
    }

    private void loginLog(String value, Object result) {
        LoginVO loginVO = new LoginVO();
        if (result instanceof LoginVO) {
            loginVO = (LoginVO) result;
        }
        UserVO userVO = loginVO.getUserVO();
        //1.查出user
        log.error("userVO:{}",userVO);
        User one = userService.getOne(new LambdaQueryWrapper<User>().eq(User::getUsername, userVO.getUsername()));
        if(Objects.isNull(one)){
            return;
        }
        UserLog userLog = BeanUtil.copyProperties(one, UserLog.class, "id");
        //2.封装userLog
        Device device = getDevice();
        userLog.setUserId(one.getId());
        userLog.setOpDetail(value);
        userLog.setUserPhone(one.getPhone());
        userLog.setUserEmail(one.getEmail());
        userLog.setOpIp(device.clientIP);
        userLog.setOpOs(device.os);
        userLog.setOpTime(LocalDateTime.now());
        //3. 任务加入线程池
        AsyncManager.me().execute(new TimerTask() {
            @Override
            public void run() {
                userLogService.save(userLog);
            }
        });
    }

    private void registerLog(String value, Object result) {
        // 填充日志对象
        User user = new User();
        if (result instanceof User) {
            user = (User) result;
        }
        //1. result就是注册后的用户对象
        Device device = getDevice();
        UserLog userLog = BeanUtil.copyProperties(user, UserLog.class, "id");
        userLog.setUserId(user.getId()); //对应关系
        userLog.setUserPhone(user.getPhone());
        userLog.setUserEmail(user.getEmail());
        userLog.setOpDetail(value);
        userLog.setOpOs(device.os);
        userLog.setOpIp(device.clientIP);
        userLog.setOpTime(LocalDateTime.now());
        //2. 执行异步保存
        AsyncManager.me().execute(new TimerTask() {
            @Override
            public void run() {
                userLogService.save(userLog);
            }
        });
    }


    /**
     * 获取设备ip，系统
     *
     * @return device
     */
    @NotNull
    private static Device getDevice() {
        // 获取request
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();
        if (Objects.isNull(request.getHeader("User-Agent"))) {
            return new Device("测试ip", "测试os");
        }
        //获取ip地址等
        UserAgent parse = UserAgentUtil.parse(request.getHeader("User-Agent"));
        String clientIP = ServletUtil.getClientIP(request);
        String os = parse.getOs().toString();
        Device device = new Device(clientIP, os);
        return device;
    }

    /**
     * 设备ip 系统类
     */
    private static class Device {
        public final String clientIP;
        public final String os;

        public Device(String clientIP, String os) {
            this.clientIP = clientIP;
            this.os = os;
        }
    }
}
